package com.coolw.test.sftp.pool;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import lombok.SneakyThrows;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.dromara.hutool.core.text.StrUtil;

import java.io.File;
import java.util.Properties;

/**
 * sftp连接池工厂，定制ChannelSftp对象的创建、校验和销毁逻辑
 *
 * @author coolw
 * @date 2025/8/12 11:24
 */
public class SftpPoolFactory extends BasePooledObjectFactory<ChannelSftp> {

    private final SftpProperties properties;

    public SftpPoolFactory(SftpProperties properties) {
        this.properties = properties;
    }

    /**
     * 创建对象
     */
    @Override
    public ChannelSftp create() throws Exception {
        JSch jsch = new JSch();

        // 优先使用私钥认证
        if (StrUtil.isNotBlank(properties.getPrivateKey())) {
            File privateKeyFile = new File(properties.getPrivateKey());
            if (privateKeyFile.exists() && privateKeyFile.isFile()) {
                if (StrUtil.isNotBlank(properties.getPassphrase())) {
                    jsch.addIdentity(properties.getPrivateKey(), properties.getPassphrase());
                } else {
                    jsch.addIdentity(properties.getPrivateKey());
                }
            }
        }

        // 创建会话
        Session session = jsch.getSession(
                properties.getUsername(),
                properties.getHost(),
                properties.getPort()
        );

        // 使用密码认证（如果未配置私钥）
        if (StrUtil.isBlank(properties.getPrivateKey())) {
            session.setPassword(properties.getPassword());
        }

        // 配置会话
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        session.connect();

        // 打开SFTP通道
        Channel channel = session.openChannel("sftp");
        channel.connect();
        return (ChannelSftp) channel;
    }

    @Override
    public PooledObject<ChannelSftp> wrap(ChannelSftp channel) {
        return new DefaultPooledObject<>(channel);
    }

    /**
     * 校验对象是否有效
     * <pre>
     *  空闲对象检测：{@link GenericObjectPoolConfig#getTestWhileIdle()} 为true时生效
     *  创建对象：{@link GenericObjectPoolConfig#getTestOnCreate()} 为true时生效
     *  借用对象：{@link GenericObjectPoolConfig#getTestOnBorrow()} 为true时生效
     *  归还对象：{@link GenericObjectPoolConfig#getTestOnReturn()} 为true时生效
     * </pre>
     */
    @SneakyThrows
    @Override
    public boolean validateObject(PooledObject<ChannelSftp> p) {
        ChannelSftp channel = p.getObject();
        return channel.isConnected() && channel.getSession().isConnected();
    }

    /**
     * 归还对象时，如果对象无效或连接池已满则销毁对象 {@link GenericObjectPoolConfig#getTestOnReturn()} 为true时才生效
     */
    @Override
    public void destroyObject(PooledObject<ChannelSftp> p) throws Exception {
        ChannelSftp channel = p.getObject();
        if (channel.isConnected()) {
            channel.disconnect();
        }
        Session session = channel.getSession();
        if (session.isConnected()) {
            session.disconnect();
        }
    }
}
